package org.hj.modules.generatecode.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import freemarker.template.Template;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.io.IOUtils;
import org.hj.common.annotation.AutoLog;
import org.hj.common.api.vo.Result;
import org.hj.modules.generatecode.entity.ViewModule;
import org.hj.modules.generatecode.entity.ViewUiproperties;
import org.hj.modules.generatecode.service.IViewModuleService;
import org.hj.modules.generatecode.service.IViewUipropertiesService;
import org.hj.modules.generatecode.util.DatabaseUtil;
import org.hj.modules.generatecode.util.FreeMarkerTemplateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Description: 代码生成
 * @Author: gaobw
 * @Date: 2020-06-16
 * @Version: V1.0
 */
@Slf4j
@Api(tags = "代码生成")
@RestController
@RequestMapping("/generatecode")
public class CodeGenerateController {
    @Autowired
    private IViewUipropertiesService viewUipropertiesService;

    private DatabaseUtil databaseUtil;


    @Autowired
    private IViewModuleService viewModuleService;


    private final String diskPath = "";


    /**
     * 生成代码
     *
     * @param
     * @return
     */
    @AutoLog(value = "代码生成")
    @ApiOperation(value = "代码生成", notes = "代码生成")
    @GetMapping(value = "/getCode")
    public void queryById(@RequestParam(name = "tableName", required = true) String tableName,
                          @RequestParam(name = "parentId", required = true) String parentId,
                          HttpServletRequest request, HttpServletResponse response) {
        Result<ViewUiproperties> result = new Result<ViewUiproperties>();

        // 用于将数据压缩成Zip文件格式
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);

        try {
            //查询基本信息
            Map<String, Object> dataMap = getInformation(parentId, tableName,zip);

            response.reset();
            response.setHeader("Content-Disposition", "attachment; filename=\"generator-code.zip\"");
            response.addHeader("Content-Length", "" + outputStream.toByteArray().length);
            response.setContentType("application/octet-stream; charset=UTF-8");
            IOUtils.write(outputStream.toByteArray(), response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    /**
     * 查询基本信息，返回代码生成设置的参数
     *
     * @param parentId
     * @param tableName
     * @return
     */
    private Map<String, Object> getInformation(String parentId, String tableName,ZipOutputStream zip) {

        String changeTableName = replaceUnderLineAndUpperCase(tableName);
        //查询表设置
        QueryWrapper<ViewModule> wrapper = new QueryWrapper<>();
        wrapper.eq("view_maintable", tableName);
        wrapper.eq("id", parentId);
        ViewModule viewModule = viewModuleService.getOne(wrapper);

        String packagePath = viewModule.getViewPacking();
        packagePath = packagePath.replace(".", "/");
        viewModule.setViewUploadFlag("1");
        viewModuleService.updateById(viewModule);

        //查询字段设置
        QueryWrapper<ViewUiproperties> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("ui_table", tableName);
        queryWrapper.eq("parent_id", parentId);
        queryWrapper.orderByAsc("ui_order");
        List<ViewUiproperties> list = viewUipropertiesService.list(queryWrapper);

        queryWrapper= new QueryWrapper<>();
        queryWrapper.eq("ui_table", tableName);
        queryWrapper.eq("ui_appview", "true");
        queryWrapper.eq("parent_id", parentId);
        queryWrapper.orderByAsc("ui_order");
        List<ViewUiproperties> formlist = viewUipropertiesService.list(queryWrapper);


        QueryWrapper<ViewUiproperties> wrapper1 = new QueryWrapper<>();
        wrapper1.eq("ui_table", tableName);
        wrapper1.eq("ui_form", "true");
        wrapper1.eq("parent_id", parentId);
        wrapper1.orderByAsc("ui_order");
        List<ViewUiproperties> list1 = viewUipropertiesService.list(wrapper1);
        Map<String, Object> dataMap = new HashMap<>();
        //原始表名
        dataMap.put("name", tableName);
        //文件包名地址
        dataMap.put("packagePath", packagePath);
        //转换后的表名
        dataMap.put("tableName", changeTableName);
        //生成模板时用来判断是否是主键id
        dataMap.put("primaryKeyField", "id");
        //转换后的表名，用来作为实体bean的文件名
        dataMap.put("entityName", changeTableName);
        //主表的设置信息
        dataMap.put("viewModule", viewModule);
        //详细字段设置，用来生成列表页面
        dataMap.put("originalColumns", list);
        //用来生成查询条件
        dataMap.put("formColumns", formlist);
        //主表的详细字段设置，用来生成新增页面
        dataMap.put("formList", list1);

        /**
         * 判断是否是主明细表的生成模式，
         * 如果是，就查询对应的明细表的数据
         */

        if (viewModule.getDetailedTable() != null && viewModule.getDetailedTable().length() > 0) {
            //判断生成类型，0代表生成单表，1代表生成主明细表
            dataMap.put("generateType", "1");

            //查询字段设置,主明细表所有字段都查询出来
            QueryWrapper<ViewUiproperties> queryWrapperAll = new QueryWrapper<>();
            queryWrapperAll.eq("parent_id", parentId);
            queryWrapperAll.orderByAsc("ui_order");
            List<ViewUiproperties> listAll = viewUipropertiesService.list(queryWrapperAll);
            dataMap.put("allColumns", listAll);

            //明细表的表名，转换
            String[] detailedTable = viewModule.getDetailedTable().split(",");
            String str = Arrays.stream(detailedTable).map(s -> replaceUnderLineAndUpperCase(s)).collect(Collectors.joining(",", "", ""));
            dataMap.put("detailedTable", str.split(","));

            //设置关联表的主键
            dataMap.put("fk", "fk_id");
            dataMap.put("FK", "FkId");


            for(String det:detailedTable){

                //查询字段设置
                QueryWrapper<ViewUiproperties> qw = new QueryWrapper<>();
                qw.eq("ui_table", det);
                qw.eq("parent_id", parentId);
                qw.orderByAsc("ui_order");
                List<ViewUiproperties> detlist = viewUipropertiesService.list(qw);

                Map<String, Object> data = new HashMap<>();
                //原始表名
                data.put("name", det);
                //文件包名地址
                data.put("packagePath", packagePath);
                //转换后的表名
                data.put("tableName", replaceUnderLineAndUpperCase(det));
                //生成模板时用来判断是否是主键id
                data.put("primaryKeyField", "id");
                //转换后的表名，用来作为实体bean的文件名
                data.put("entityName", replaceUnderLineAndUpperCase(det));
                //主表的设置信息
                data.put("viewModule", viewModule);
                //详细字段设置，用来生成列表页面
                data.put("originalColumns", detlist);
                //设置关联表的主键
                data.put("fk", "fk_id");
                data.put("FK", "FkId");

                try {
                    generateDetailed(det, data, zip);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }

            try {
                generateOneToMany(tableName, dataMap, zip);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            dataMap.put("generateType", "0");
            try {
                generateOne(tableName, dataMap, zip);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return dataMap;
    }

    /**
     *
     * 主明细表中——主表的生成的模板
     *
     * @param tableName
     * @param zip
     * @throws Exception
     */
    public void generateOneToMany(String tableName, Map<String, Object> dataMap, ZipOutputStream zip) throws Exception {
        try {
            //生成Mapper.xml文件
            generateFile(tableName, dataMap, "Mapper.xml", "detailed/mapper/mapperXml/MapperXml.ftl", zip, "mapper/xml");
            //生成entity文件
            generateFile(tableName, dataMap, ".java", "detailed/entity/entity.ftl", zip, "entity");
            generateFile(tableName, dataMap, "MainPage.java", "detailed/vo/detailedEntity.ftl", zip, "vo");
            //生成Mapper文件
            generateFile(tableName, dataMap, "Mapper.java", "detailed/mapper/Mapper.ftl", zip, "mapper");
           //生成服务层接口文件
            generateServiceInterfaceFile(tableName, dataMap, "Service.java", "detailed/service/Service.ftl",zip, "service");

            //生成服务实现层文件
            generateFile(tableName, dataMap, "ServiceImpl.java", "detailed/service/serviceImp/ServiceImpl.ftl", zip, "service/impl");
            //生成Controller层文件
            generateFile(tableName, dataMap, "Controller.java", "detailed/controller/Controller.ftl", zip, "controller");

            //生成vue列表文件
            generateFile(tableName, dataMap, "List.vue", "detailed/vue/VueList.ftl", zip, "vue");
            //生成vue表单文件
            generateFile(tableName, dataMap, "Modal.vue", "detailed/vue/modules/VueModal.ftl", zip, "vue/modules");


        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {

        }
    }

    /**
     *
     * 主明细表中——明细表的生成的模板
     *
     * @param tableName
     * @param zip
     * @throws Exception
     */
    public void generateDetailed(String tableName, Map<String, Object> dataMap, ZipOutputStream zip) throws Exception {
        try {
            //生成entity文件
            generateFile(tableName, dataMap, ".java", "detailed/entity/entity.ftl", zip, "entity");
            //生成服务层接口文件
            generateServiceInterfaceFile(tableName, dataMap, "Service.java", "detailed/service/detailedService.ftl",zip, "service");
            //生成服务实现层文件
            generateFile(tableName, dataMap, "ServiceImpl.java", "detailed/service/serviceImp/detailedServiceImpl.ftl", zip, "service/impl");
            //生成Mapper文件
            generateFile(tableName, dataMap, "Mapper.xml", "detailed/mapper/mapperXml/detailedMapperXml.ftl", zip, "mapper/xml");
            //生成Mapper.xml文件
            generateFile(tableName, dataMap, "Mapper.java", "detailed/mapper/detailedMapper.ftl", zip, "mapper");

        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {

        }
    }

    /**
     *单表生成的模板
     *
     *
     * @param tableName
     * @param zip
     * @throws Exception
     */
    public void generateOne(String tableName, Map<String, Object> dataMap, ZipOutputStream zip) throws Exception {
        try {
            //生成Mapper.xml文件
            generateFile(tableName, dataMap, "Mapper.xml", "MapperXml.ftl", zip, "mapper/xml");
            //生成entity文件
            generateFile(tableName, dataMap, ".java", "entity.ftl", zip, "entity");
            //生成Mapper文件
            generateFile(tableName, dataMap, "Mapper.java", "Mapper.ftl", zip, "mapper");
            //生成服务层接口文件
            generateServiceInterfaceFile(tableName, dataMap, "Service.java", "Service.ftl",zip, "service");
            //生成服务实现层文件
            generateFile(tableName, dataMap, "ServiceImpl.java", "ServiceImpl.ftl", zip, "service/impl");
            //生成Controller层文件
            generateFile(tableName, dataMap, "Controller.java", "Controller.ftl", zip, "controller");
            //生成vue列表文件
            generateFile(tableName, dataMap, "List.vue", "VueList.ftl", zip, "vue");
            //生成vue表单文件
            generateFile(tableName, dataMap, "Modal.vue", "VueModal.ftl", zip, "vue/modules");


        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {

        }
    }





    private void generateFile(String tableName, Map<String, Object> dataMap, String suffix, String templateName, ZipOutputStream zip, String catalogPath) throws Exception {
        String changeTableName = replaceUnderLineAndUpperCase(tableName);
        final String path = diskPath + changeTableName + suffix;
        generateFileByTemplate(templateName, dataMap, path, zip, catalogPath);
    }


    private void generateServiceInterfaceFile(String tableName, Map<String, Object> dataMap,String suffix, String templateName, ZipOutputStream zip, String catalogPath) throws Exception {
        String changeTableName = replaceUnderLineAndUpperCase(tableName);
        final String prefix = "I";
        final String path = diskPath + prefix + changeTableName + suffix;
        generateFileByTemplate(templateName, dataMap, path, zip, catalogPath);
    }

    private void generateFileByTemplate(final String templateName, Map<String, Object> dataMap, String path, ZipOutputStream zip, String catalogPath) throws Exception {


        Template template = FreeMarkerTemplateUtils.getTemplate(templateName);
        val stringWriter = new StringWriter();
        template.process(dataMap, stringWriter);

        String packagePath = dataMap.get("packagePath").toString();
        //添加到zip
        zip.putNextEntry(new ZipEntry(packagePath + File.separator + catalogPath + File.separator + path));
        IOUtils.write(stringWriter.toString(), zip, "UTF-8");
        // 向压缩文件中输出数据
        zip.closeEntry();
    }

    //字符转换 sys_user => SysUser
    public static String replaceUnderLineAndUpperCase(String str) {
        StringBuffer sb = new StringBuffer();
        sb.append(str);
        int count = sb.indexOf("_");
        while (count != 0) {
            int num = sb.indexOf("_", count);
            count = num + 1;
            if (num != -1) {
                char ss = sb.charAt(count);
                char ia = (char) (ss - 32);
                sb.replace(count, count + 1, ia + "");
            }
        }
        String result = sb.toString().replaceAll("_", "");
        return StringUtils.capitalize(result);
    }


}
